package com.guiji.text2video.fastvideo.task;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.guiji.text2video.common.core.utils.StringUtils;
import com.guiji.text2video.fastvideo.domain.vo.Text2videoConfig;
import com.guiji.text2video.fastvideo.domain.vo.SpiderNews;
import com.guiji.text2video.fastvideo.domain.vo.Text2videoJob;
import com.guiji.text2video.fastvideo.mapper.*;
import com.guiji.text2video.fastvideo.service.IText2videoJobService;
import com.guiji.text2video.fastvideo.service.IText2videoZipJobService;
import com.guiji.text2video.fastvideo.service.Impl.Text2videoJobServiceImpl;
import com.guiji.text2video.fastvideo.service.Impl.VideoComposeService;
import com.guiji.text2video.fastvideo.utils.Enums;
import com.guiji.text2video.fastvideo.utils.GeneratID;
import com.guiji.text2video.fastvideo.utils.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * Copyright (C), 2018-2021
 * FileName: CommitNewsTimingTask
 * Author:   scl
 * Date:     2021/7/19 13:37
 * Description: 新闻类型定时任务
 */
@Component("commitNewsTimingTask")
public class CommitNewsTimingTask {

    private static final Logger logger = LoggerFactory.getLogger(CommitNewsTimingTask.class);

    private List<Text2videoConfig> chinaNews;

    private List<Text2videoConfig> englishNews;

    private Map<String, Integer> eachThresholds = new HashMap<>();

    List<Text2videoConfig> lowUploadsList = new ArrayList<>();
    List<Text2videoConfig> highUploadsList = new ArrayList<>();

    private static final int defaultThreshold = 5;


    @Value("${file-cache.task-video-dir}")
    private String taskVideoPath;

    @Value("${file-cache.news-speaker}")
    private String newsSpeaker;

    @Value("${file-cache.english-news-speaker}")
    private String englishNewsSpeaker;

    @Value("${file-cache.news-default-pic}")
    private String newsDefaultPicture;

    @Value("${file-cache.news-rate}")
    private int newsRate;

    @Value("${file-cache.wav-dir}")
    private String wavDir;

    @Value("${file-cache.news-create-url}")
    private String newsCreateUrl;

    @Value("${file-cache.news-add-url}")
    private String newsAddUrl;

    @Value("${file-cache.news-youtubo-rtmp}")
    private String newsYouTube;

    @Value("${file-cache.news-default-video}")
    private String newsDefaultVideo;

    @Value("${file-cache.news-timeout}")
    private int newsTimeOut;

    @Value("${file-cache.transform-url}")
    private String transformUrl;


    @Autowired(required = false)
    SpiderNewsMapper spiderNewsMapper;

    @Autowired(required = false)
    SpiderNewsStatMapper spiderNewsStatMapper;

    @Autowired(required = false)
    Text2videoJobMapper jobMapper;

    @Autowired(required = false)
    Text2videoPoolsStatMapper pools;

    @Autowired(required = false)
    Text2videoNewsSessionMapper newsSessionMapper;

    @Autowired
    IText2videoJobService jobService;

    @Autowired
    IText2videoZipJobService iText2videoZipJobService;

    @Autowired
    Text2videoJobServiceImpl text2videoJobServiceImpl;

    @Autowired
    VideoComposeService videoComposeService;

    @Autowired(required = false)
    Text2videoConfigMapper configMapper;

    @Value("${spring.profiles.active}")
    private String env;


    /**
     * @descp: 集合排序
     * @param:
     * @Author: scl
     * @Date: 2021/11/23 14:44
     */
    private List<Text2videoConfig> getOrderedList(List<Text2videoConfig> list) {
        Collections.sort(list, new Comparator<Text2videoConfig>() {
            @Override
            public int compare(Text2videoConfig o1, Text2videoConfig o2) {
                //排序属性
                if (o1.getNewsPriority() < o2.getNewsPriority()) {
                    return 1;
                }
                if (o1.getNewsPriority() == o2.getNewsPriority()) {
                    return 0;
                }
                return -1;
            }
        });
        return list;
    }

    /**
     * @descp: 排序功能
     * @param: 定时执行取出list
     * 每天执行一次，也可以手动触发
     * @Author: scl
     * @Date: 2021/11/23 14:23
     */
    private void setList() {

        Text2videoConfig config = new Text2videoConfig();

        config.setNewsLang(0);
        chinaNews = getOrderedList(configMapper.selectText2videoConfigList(config));
        logger.info("中文配置已更新:{}", chinaNews.size());
        config.setNewsLang(1);
        englishNews = getOrderedList(configMapper.selectText2videoConfigList(config));
        logger.info("英文配置已更新:{}", englishNews.size());

    }

    /**
     * @descp: 每60秒执行一次任务扫描, 开发环境关闭定时任务
     * @param:
     * @Author: scl
     * @Date: 2021/7/19 13:42
     */
    @Scheduled(cron = "25/60 * * * * *")
    public void commitTodayNewsTask() {
        if ("prod".equals(env)) {
            commitBatchNewsJobs(DateUtil.today(), chinaNews);
        }
    }

    /**
     * @descp: 每10分钟执行一次任务扫描,
     * @param:
     * @Author: scl
     * @Date: 2021/7/19 13:42
     */
    @Scheduled(cron = "0 0/2 * * * ?")
    public void commitYesterdayNewsTask() {
        if ("prod".equals(env)) {
            commitBatchNewsJobs(null, chinaNews);
        }
    }

    /**
     * @descp: 每60秒执行一次英文任务扫描, 开发环境关闭定时任务
     * @param:
     * @Author: scl
     * @Date: 2021/7/19 13:42
     */
    //@Scheduled(cron = "15/60 * * * * *")
    public void commitTodayEnglishNewsTask() {
        if ("prod".equals(env)) {
            commitBatchNewsJobs(DateUtil.today(), englishNews);
        }
    }


    private void commitBatchNewsJobs(String day, List<Text2videoConfig> list) {
        //当前时间在0-8点，终止方法。腾出时间-二创任务。
/*        if (DateUtil.hour(new Date(), true) < 1) {
            return;
        }*/
        if (ObjectUtil.isEmpty(list)) {
            logger.info("没有类别集合");
            setList();
        }

        if (ObjectUtils.isNotEmpty(pools.queryLastPoolsNewsCompose())) {
            return;
        }
        refreshEachThresholds();
        jobCommit(8, day, lowUploadsList, highUploadsList);
    }


    private void jobCommit(int commitSize, String day, List<Text2videoConfig> lowUploadsList, List<Text2videoConfig> highUploadsList) {

        //1,每次提交5个新闻文本,历史新闻也可以
        if (runEachConfigNewsUntilLimit(commitSize, null, lowUploadsList)) {
            return;
        }
        // 先执行不饱和的config
        logger.info("低于阀值类别没有可提交的任务,(越小越好)lowUploadsList大小:{}", lowUploadsList.size());
        runEachConfigNewsUntilLimit(commitSize, day, highUploadsList);
    }

    /**
     * @descp: 提交news任务直至 单个config达到commitSize
     * @param:
     * @Author: wangxw
     * @Date: 2021/12/21 14:27
     */
    private boolean runEachConfigNewsUntilLimit(int commitSize, String day, List<Text2videoConfig> configList) {
        if (null == day) {
            logger.info("null=day,config数量:{}", configList.size());
        }
        int taskNum = 0;
        long startTime = System.currentTimeMillis();
        for (Text2videoConfig config : configList) {
            SpiderNews spiderNews = new SpiderNews();
            spiderNews.setNewsCategory(config.getNewsCategory());
            spiderNews.setBackup1(day);
            List<SpiderNews> categoryList = spiderNewsMapper.selectSpiderNewsList(spiderNews);
            for (SpiderNews news : categoryList) {
                if (ObjectUtils.isEmpty(news.getBackup1())) {
                    simpleCommit(news, config);
                    taskNum++;
                    if (taskNum == commitSize) {
                        logger.info("消耗时间:{}ms,执行类别:{}", System.currentTimeMillis() - startTime, config.getNewsCategory());
                        return true;
                    }
                }
            }
        }
        logger.info("消耗时间:{}ms", System.currentTimeMillis() - startTime);
        return false;
    }

    /**
     * @descp: 刷新eachThresholds
     * @param: 账号改版：早上类别，下午类别不一样。
     * config最新，stat+job全天记录。
     * @Author: wangxw
     * @Date: 2021/12/20 16:43
     */
    private void refreshEachThresholds() {

        Map<String, Integer> tmpEachThresholds = new HashMap<>();
        List<Text2videoConfig> fullConfigs = new ArrayList<>();
        if (null == chinaNews) {
            setList();
        }
        fullConfigs.addAll(chinaNews);
        fullConfigs.addAll(englishNews);

        List<Map<String, Long>> acctNumKvs = spiderNewsStatMapper.selectThresholdMap();
        logger.info("fullConfigs数据大小:{}", fullConfigs.size());
        logger.info("job+stat表统计数据大小:{}", acctNumKvs.size());
        StringBuffer tuisongStr = new StringBuffer();
        if (null != acctNumKvs && acctNumKvs.size() > 0) {
            for (Map<String, Long> acctNumKv : acctNumKvs) {
                if (null != acctNumKv.get("news_category")) {
                    String newsCategory = String.valueOf(acctNumKv.get("news_category")).trim();
                    tuisongStr.append(newsCategory + ";");
                    for (Text2videoConfig text2videoConfig : fullConfigs) {
                        if (newsCategory.equals(text2videoConfig.getNewsCategory().trim())) {
                            tmpEachThresholds.put(newsCategory, Integer.parseInt(String.valueOf(acctNumKv.get("total"))));
                        }
                    }
                }
            }
            eachThresholds = tmpEachThresholds;

            List<Text2videoConfig> tmplowUploadsList = fullConfigs;
            List<Text2videoConfig> tmphighUploadsList = new ArrayList<>();
            Iterator<Text2videoConfig> iterator = tmplowUploadsList.iterator();
            while (iterator.hasNext()) {
                Text2videoConfig config = iterator.next();
                if (isContinueByThreshold(config)) {
                    tmphighUploadsList.add(config);
                    iterator.remove();
                }
            }
            lowUploadsList = tmplowUploadsList;
            highUploadsList = tmphighUploadsList;
            StringBuffer high = new StringBuffer();
            for (Text2videoConfig c : highUploadsList) {
                high.append(c.getNewsCategory() + ";");
            }
            //优化
            StringBuffer eachThresholdsbf = new StringBuffer();
            for (Map.Entry<String, Integer> entry : eachThresholds.entrySet()) {
                eachThresholdsbf.append(entry.getKey() + "=" + entry.getValue() + ";");
            }

            StringBuffer low = new StringBuffer();
            StringBuffer lowZero = new StringBuffer();
            for (Text2videoConfig c : lowUploadsList) {
                low.append(c.getNewsCategory() + ";");
                //没有合成，但可能推送，可能不推送
                if (!eachThresholdsbf.toString().contains(c.getNewsCategory() + "=")) {
                    lowZero.append(c.getNewsCategory() + ";");
                }
            }

            logger.info("大于阀值有：{},内容：{}", highUploadsList.size(), high.toString());
            logger.info("小于阀值有：{},内容：{}", lowUploadsList.size(), low.toString());
            logger.info("没有合成有：{},内容：{}", lowZero.toString().split(";").length, lowZero.toString());
            logger.info("有合成数量：{},内容：{}", eachThresholds.size(), eachThresholdsbf.toString());
        }
    }

    /**
     * @descp: 依据阈值判断是否继续
     * @param:
     * @Author: wangxw
     * @Date: 2021/12/20 16:43
     */
    private boolean isContinueByThreshold(Text2videoConfig config) {
        int threshold = defaultThreshold;
        String num = config.getBackup4();
        if (num != null) {
            threshold = Integer.parseInt(num.trim());
        }
        if (null != eachThresholds && eachThresholds.get(config.getNewsCategory()) != null && eachThresholds.get(config.getNewsCategory()) >= threshold) {
            return true;
        }

        return false;
    }


    /**
     * @descp: 单个任务发起
     * @param:
     * @Author: scl
     * @Date: 2021/10/25 11:13
     */
    private void simpleCommit(SpiderNews news, Text2videoConfig config) {
        Text2videoJob job = new Text2videoJob();
        String jobId = GeneratID.getJobGeneratID();

        //防止标题超过100字符，上传填充不了
        String title = news.getNewsTitle();
        if (title.length() > 99) {
            title = title.substring(0, 100);
        }

        //过滤特殊字符
        title = title.replace("&", "");

        String content = news.getNewsContent().replace("&", "");
        //en-de-en翻译，zh-en-zh

        job.setTitle(title);
        job.setContent(content);
        job.setBackupText(news.getNewsCategory());
        job.setEndTime(news.getNewsPublicTime());
        job.setJobId(jobId);
        //区别中英文新闻
        Boolean isPic = (ObjectUtils.isEmpty(news.getNewsPicJson()) || news.getNewsPicJson().equals("[]"));
        //没有图片，随机填充
        if (isPic) {
            String[] picArray = config.getTitlePic().split(",");
            int a = (int) (Math.random() * (picArray.length));
            job.setBackup4(picArray[a]);
        } else {
            job.setBackup4(news.getNewsPicJson().replace("[", "").replace("]", "").replace("\"",""));
        }

        if (Validator.hasChinese(config.getNewsCategory())) {
            job.setTtsType("-1");
        } else {
            job.setTtsType("-3");
            String tmpTitle = toTransform(job.getTitle(), "en", "zh");
            if (Validator.hasChinese(tmpTitle) || tmpTitle.contains("language_code")) {
                //表示转换失败，保持原来新闻
            } else {
                job.setTitle(tmpTitle);
            }

        }

        /**
         * @descp: tts_type 对应nlp的domain。
         * retry_size 新闻优先级
         * tts_languge 保留,（标记scp状态,什么）,特效参数
         * backup1 服务端ip,标记使用的数字人
         * backup2 下载链接,错误提示,数字人参数
         * backup3 背景音乐
         * backup4 保留,新闻图片
         * backup5 保留,362
         * @param: simpleCommit
         * @Author: scl
         * @Date: 2021/11/23 15:15
         */
        job.setTtsLanguage(config.getContentPic());
        job.setTtsSpeaker(config.getTtsSpeaker());
        job.setTtsRate(config.getTtsRate());
        job.setBackup1(config.getDigitalName());
        job.setBackup2(config.getDigitalHuman());
        //背景音乐
        if(StringUtils.isNotEmpty(config.getCreateBy()) && config.getCreateBy().contains("http")){
            job.setBackup3(config.getCreateBy());
        }
        job.setBackup5(config.getNewsScene());

        job.setRetrySize(config.getNewsPriority());
        job.setZipId(news.getNewsId());
        job.setIsSuccess("0");
        text2videoJobServiceImpl.insertText2videoJob(job);
        news.setBackup1(jobId);
        //标题发生变化
        if (!news.getNewsTitle().equals(job.getTitle())) {
            //存储在backup2上
            news.setBackup2(job.getTitle());
        }
        spiderNewsMapper.updateSpiderNews(news);
        //提交新闻任务
        iText2videoZipJobService.muiltyNewsCommit(job);
        logger.info("发起新闻任务: {}:{}:{}", news.getBackup1(), news.getNewsCategory(), news.getNewsPublicTime());

    }


    /**
     * @descp: 更新配置表
     * @param: 更新饱和list, 不饱和list
     * @Author: scl
     * @Date: 2021/10/25 14:35
     */
    public void oneTypeNews(Text2videoJob job) {
        setList();
        refreshEachThresholds();
    }

    /**
     * @descp: 每60秒执行一次任务扫描, 检测新闻队列，如果为空则推送历史
     * @param:
     * @Author: scl
     * @Date: 2021/7/29 13:42
     */
    //@Scheduled(cron = "20/60 * * * * *")
    public void checkQueueNews() {
        //取出可用session
        String sessionId = videoComposeService.getAbledSession();
        //判断队列
        String queue = newsAddUrl.replace("add", "queue");
        HttpResponse queueResponse = HttpRequest.post(queue + sessionId).timeout(newsTimeOut).body("").executeAsync();
        if (queueResponse.getStatus() == Enums.numSign.len404.getKey()) {
            logger.info("定时任务使用存在的session失败-删除：{}", sessionId);
            newsSessionMapper.deleteText2videoNewsSessionBySessionId(sessionId);
        }
        if (queueResponse.getStatus() == Enums.numSign.len200.getKey()) {
            if (ObjectUtils.isNotEmpty(queueResponse.body()) && queueResponse.body().length() > 30) {
                return;
            }
            //放入高优先级视频,24h之内的新闻视频
            Text2videoJob job = new Text2videoJob();
            job.setIsSuccess("1");
            job.setTtsType("-1");
            List<Text2videoJob> listJobs = jobMapper.selectText2videoJobListByEndTime(job);
            for (Text2videoJob videoJob : listJobs) {
                HttpResponse pushResponse = HttpRequest.post(newsAddUrl + sessionId).timeout(newsTimeOut).body(videoJob.getBackup4()).executeAsync();
                if (pushResponse.getStatus() == Enums.numSign.len200.getKey()) {
                    logger.info("新闻历史视频推送成功:{}", videoJob.getBackup4());
                }
            }
        }

    }

    /**
     * @descp: 每10min执行一次刪除历史
     * @param:0 0 10,22 * * ?
     * 0 0/10 * * * ?
     * @Author: scl
     * @Date: 2021/7/29 13:42
     */
    @Scheduled(cron = "0 0 10,22 * * ?")
    public void delete24History() {

        if ("dev".equals(env)) {
            return;
        }
        String videoPath = taskVideoPath;
        videoPath = videoPath.replace("taskVideos", "news");

        //删除24h之前所有新闻。删除文件。
        Text2videoJob job2 = new Text2videoJob();
        job2.setTtsType("-1");
        List<Text2videoJob> listJobAll = jobMapper.selectText2videoJobListBy24H(job2);
        Text2videoJob jobEnglish = new Text2videoJob();
        jobEnglish.setTtsType("-3");
        List<Text2videoJob> listJobEnglish = jobMapper.selectText2videoJobListBy24H(jobEnglish);
        listJobAll.addAll(listJobEnglish);
        for (Text2videoJob job3 : listJobAll) {

            if (job3.getIsSuccess().equals("1")) {
                String saveFile = videoPath + job3.getJobId() + ".mp4";
                logger.info("每天10点执行,删除7天之前的高优先级视频:{}", saveFile);
                FileUtil.del(saveFile);
            }
            //清空历史表nlp,tts,compose,保留job表
            logger.info("每天10点执行,清空历史表nlp,tts,compose,保留job表:{}", job3.getJobId());
            jobService.deleteJobHistoryNlpTTSCompose(job3.getJobId());
            jobMapper.deleteText2videoJobById(job3.getJobId());
        }
    }


    /**
     * @descp: 翻译转换en-de-en,zh-en-zh
     * @param:
     * @Author: scl
     * @Date: 2021/12/30 14:13
     */
    private String toTransform(String bytes, String source, String middle) {
        return tranformBytes(tranformBytes(bytes, middle), source);
    }

    private String tranformBytes(String bytes, String en) {

        String reruest = "{\"text\":\"" + bytes + "\",\"language_code\":\"" + en + "\"}";
        HttpResponse httpResponse = null;
        try {
            httpResponse = HttpRequest.post(transformUrl).timeout(600000).body(reruest).executeAsync();
        } catch (Exception e) {
            logger.info("翻译服务异常中断");
            throw new HttpException("翻译服务异常中断");
        }

        if (null != httpResponse && httpResponse.getStatus() == Enums.numSign.len200.getKey()) {
            return httpResponse.body();
        } else {
            return bytes;

        }
    }

}
